package geecache

import (
	"fmt"
	"log"
	"sync"
)

//  负责与外部交互，控制缓存存储和获取的主流程


// 这是一个回调函数，在缓存未命中时，获取源数据
// 定义一个函数类型F,并且实现接口A的方法，然后在这个方法中调用自己
// 这就是go语言中将其它函数（参数返回值定义与F一致）转换为接口A的常用技巧
type Getter interface {
	Get(key string) ([]byte, error)
}

type GetterFunc func(key string) ([]byte, error)

func (f GetterFunc) Get(key string) ([]byte, error){
	return f(key)
}


var (
	mu sync.RWMutex
	groups = make(map[string]*Group)
)

// 一个Group可以认为是一个缓存的命名空间，每个Group拥有一个唯一的名称name
// 比如可以创建三个group,缓存学生成绩的命名为scores, 缓存学生信息的命名info, 缓存学生课程的为courses
type Group struct {
	name string  // 名称
	getter Getter  // 缓存未命中时获取数据源的回调(callback)
	mainCache cache
}

// 创建一个Group
func NewGroup(name string, cacheBytes int64, getter Getter) *Group{
	if getter == nil{
		panic("nil Getter")
	}
	mu.Lock()
	defer mu.Unlock()

	g := &Group{
		name:      name,
		getter:    getter,
		mainCache: cache{cacheBytes:cacheBytes},
	}
	groups[name] = g
	return g
}

// 通过name获取Group
func GetGroup(name string) *Group{
	mu.RLock()
	defer mu.RUnlock()
	g := groups[name]
	return g
}


// Get value for a key from cache
// 根据key从缓存中获取value
func (g *Group) Get(key string) (ByteView, error) {
	if key == "" {
		return ByteView{}, fmt.Errorf("key is required")
	}

	if v, ok := g.mainCache.get(key); ok {
		log.Println("[GeeCache] hit")
		return v, nil
	}

	return g.load(key)
}

// 中间方法，用于调用获取本地数据源（以后可能添加其它数据源获取）
func (g *Group) load(key string) (value ByteView, err error) {
	return g.getLocally(key)
}

// 根据key从本地数据源获取数据
func (g *Group) getLocally(key string) (ByteView, error) {
	bytes, err := g.getter.Get(key)
	if err != nil {
		return ByteView{}, err

	}
	value := ByteView{b: cloneBytes(bytes)}
	g.populateCache(key, value)
	return value, nil
}

// 将数据添加进缓存
func (g *Group) populateCache(key string, value ByteView) {
	g.mainCache.add(key, value)
}




